#include <stdlib.h>
#include <string.h>

#include <common/macro/build.h>
#include <storage/blob/page_blob.h>

API_LIBLOCAL page_blob_t *page_blob_new(page_t *common) {
	page_blob_t *page_blob;

	page_blob = malloc(sizeof(*page_blob));

	page_blob->id_insert = 0;
	page_blob->id_delete = 0;
	page_blob->deleted = 0;

	page_blob->common = common;

	page_blob->data = NULL;
	page_blob->size = 0;
	page_blob->next = 0;

	page_decrease_freespace(common,
		sizeof(char) +                    /* Type */
		(sizeof(transaction_id_t) * 2) +  /* ID insert/delete */
		sizeof(char) +                    /* Deleted? */
		sizeof(page_number_t) +           /* Next */
		sizeof(page_size_t)               /* Data size */
	);

	return page_blob;
}

API_LIBLOCAL char page_blob_type_to_char(const page_blob_type_e type) {
	switch (type) {
		case BLOB_SINGLE:
			return 'S';

		case BLOB_HEAD:
			return 'H';

		case BLOB_DATA:
			return 'D';

		case BLOB_TAIL:
			return 'T';
	}

	return 'S';
}

API_LIBLOCAL page_blob_type_e page_blob_char_to_type(const char c) {
	switch (c) {
		case 'S':
			return BLOB_SINGLE;

		case 'H':
			return BLOB_HEAD;

		case 'D':
			return BLOB_DATA;

		case 'T':
			return BLOB_TAIL;
	}

	return BLOB_SINGLE;
}

API_LIBLOCAL int page_blob_serialize(page_blob_t *page_blob, char *out, ssize_t out_size) {
	char is_deleted, blob_type_char;
	ssize_t offset, itemlen;

	offset = 0;

	/* BLOB type */
	blob_type_char = page_blob_type_to_char(page_blob->type);
	itemlen = sizeof(blob_type_char);
	if (itemlen > out_size - offset) { return -1; }
	memcpy(&out[offset], &blob_type_char, itemlen);
	offset += itemlen;

	/* BLOB insert transaction ID. */
	itemlen = sizeof(page_blob->id_insert);
	if (itemlen > out_size - offset) { return -1; }
	memcpy(&out[offset], &page_blob->id_insert, itemlen);
	offset += itemlen;

	/* BLOB delete transaction ID. */
	itemlen = sizeof(page_blob->id_delete);
	if (itemlen > out_size - offset) { return -1; }
	memcpy(&out[offset], &page_blob->id_delete, itemlen);
	offset += itemlen;

	/* BLOB deleted? */
	is_deleted = (page_blob->deleted ? 0xDE : 0x00);
	itemlen = sizeof(is_deleted);
	if (itemlen > out_size - offset) { return -1; }
	memcpy(&out[offset], &is_deleted, itemlen);
	offset += itemlen;

	/* BLOB next in list. */
	itemlen = sizeof(page_blob->next);
	if (itemlen > out_size - offset) { return -1; }
	memcpy(&out[offset], &page_blob->next, itemlen);
	offset += itemlen;

	/* BLOB data size. */
	itemlen = sizeof(page_blob->size);
	if (itemlen > out_size - offset) { return -1; }
	memcpy(&out[offset], &page_blob->size, itemlen);
	offset += itemlen;

	/* BLOB data. */
	itemlen = page_blob->size;
	if (itemlen > out_size - offset) { return -1; }
	memcpy(&out[offset], page_blob->data, itemlen);
	offset += itemlen;

	return 0;
}

API_LIBLOCAL int page_blob_deserialize(page_blob_t **page_blob, char *in, ssize_t in_size, page_t *common) {
	char is_deleted, blob_type_char;
	ssize_t offset, itemlen;

	offset = 0;

	(*page_blob) = malloc(sizeof(*(*page_blob)));
	(*page_blob)->common = common;

	/* BLOB type */
	itemlen = sizeof(blob_type_char);
	if (itemlen > in_size - offset) { return -1; }
	memcpy(&blob_type_char, &in[offset], itemlen);
	offset += itemlen;
	(*page_blob)->type = page_blob_char_to_type(blob_type_char);

	/* BLOB insert transaction ID. */
	itemlen = sizeof((*page_blob)->id_insert);
	if (itemlen > in_size - offset) { return -1; }
	memcpy(&(*page_blob)->id_insert, &in[offset], itemlen);
	offset += itemlen;

	/* BLOB delete transaction ID. */
	itemlen = sizeof((*page_blob)->id_delete);
	if (itemlen > in_size - offset) { return -1; }
	memcpy(&(*page_blob)->id_delete, &in[offset], itemlen);
	offset += itemlen;

	/* BLOB deleted? */
	itemlen = sizeof(is_deleted);
	if (itemlen > in_size - offset) { return -1; }
	memcpy(&is_deleted, &in[offset], itemlen);
	offset += itemlen;
	(*page_blob)->deleted = is_deleted;

	/* BLOB next in list. */
	itemlen = sizeof((*page_blob)->next);
	if (itemlen > in_size - offset) { return -1; }
	memcpy(&(*page_blob)->next, &in[offset], itemlen);
	offset += itemlen;

	/* BLOB data size. */
	itemlen = sizeof((*page_blob)->size);
	if (itemlen > in_size - offset) { return -1; }
	memcpy(&(*page_blob)->size, &in[offset], itemlen);
	offset += itemlen;

	/* BLOB data. */
	(*page_blob)->data = &in[offset];

	return 0;
}

API_LIBLOCAL void page_blob_destroy(page_blob_t *page_blob) {
	free(page_blob);
}
